{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 🦜🪞LangGraph-Reflection\n",
    "\n",
    "This prebuilt graph is an agent that uses a reflection-style architecture to check and improve an initial agent's output.\n",
    "\n",
    "## Installation\n",
    "\n",
    "```\n",
    "pip install langgraph-reflection\n",
    "```\n",
    "\n",
    "## Details\n",
    "\n",
    "| Description | Architecture |\n",
    "|------------|--------------|\n",
    "| This reflection agent uses two subagents:<br>- A \"main\" agent, which is the agent attempting to solve the users task<br>- A \"critique\" agent, which checks the main agents work and offers any critiques<br><br>The reflection agent has the following architecture:<br><br>1. First, the main agent is called<br>2. Once the main agent is finished, the critique agent is called<br>3. Based on the result of the critique agent:<br>   - If the critique agent finds something to critique, then the main agent is called again<br>   - If there is nothing to critique, then the overall reflection agent finishes<br>4. Repeat until the overall reflection agent finishes | <img src=\"https://github.com/langchain-ai/langgraph-reflection/raw/main/langgraph-reflection.png\" alt=\"Reflection Agent Architecture\" width=\"100\"/> |\n",
    "\n",
    "We make some assumptions about the graphs:\n",
    "- The main agent should take as input a list of messages\n",
    "- The reflection agent should return a **user** message if there is any critiques, otherwise it should return **no** messages.\n",
    "\n",
    "## Examples\n",
    "\n",
    "Below are a few examples of how to use this reflection agent.\n",
    "\n",
    "### LLM-as-a-Judge \n",
    "\n",
    "In this example, the reflection agent uses another LLM to judge its output. The judge evaluates responses based on:\n",
    "1. Accuracy - Is the information correct and factual?\n",
    "2. Completeness - Does it fully address the user's query?\n",
    "3. Clarity - Is the explanation clear and well-structured?\n",
    "4. Helpfulness - Does it provide actionable and useful information?\n",
    "5. Safety - Does it avoid harmful or inappropriate content?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example query that might need improvement\n",
    "example_query = [\n",
    "    {\n",
    "        \"role\": \"user\",\n",
    "        \"content\": \"Explain how nuclear fusion works and why it's important for clean energy\",\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"Example of LLM as a judge reflection system.\n",
    "\n",
    "Should install: pip install langgraph-reflection langchain openevals\n",
    "\"\"\"\n",
    "\n",
    "from typing import TypedDict\n",
    "\n",
    "from langchain.chat_models import init_chat_model\n",
    "\n",
    "from langgraph.graph import StateGraph, MessagesState, START, END\n",
    "from langgraph_reflection import create_reflection_graph\n",
    "from openevals.llm import create_llm_as_judge"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Define the main assistant model that will generate responses\n",
    "def call_model(state):\n",
    "    \"\"\"Process the user query with a large language model.\"\"\"\n",
    "    model = init_chat_model(model=\"openai:gemini-pro\")\n",
    "    return {\"messages\": model.invoke(state[\"messages\"])}\n",
    "\n",
    "\n",
    "# Define a basic graph for the main assistant\n",
    "assistant_graph = (\n",
    "    StateGraph(MessagesState)\n",
    "    .add_node(call_model)\n",
    "    .add_edge(START, \"call_model\")\n",
    "    .add_edge(\"call_model\", END)\n",
    "    .compile()\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(assistant_graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Process the query through the raw graph\n",
    "result = assistant_graph.invoke({\"messages\": example_query})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "Let's break down nuclear fusion and its importance as a clean energy source.\n",
       "\n",
       "**What is Nuclear Fusion?**\n",
       "\n",
       "At its core, nuclear fusion is the process of **combining** two light atomic nuclei to form a heavier nucleus.  This is the opposite of nuclear fission, where a heavy nucleus is split into lighter ones.  Think of it like merging two small water droplets to create a larger one.\n",
       "\n",
       "**How Does it Work?**\n",
       "\n",
       "Here's a step-by-step breakdown of the fusion process, focusing on the type of fusion most promising for energy production (deuterium-tritium fusion):\n",
       "\n",
       "1. **The Ingredients:** The most common and readily available fuels for fusion are isotopes of hydrogen:\n",
       "   * **Deuterium (²H):** Hydrogen with one proton and one neutron in its nucleus. It's abundant in seawater.\n",
       "   * **Tritium (³H):** Hydrogen with one proton and two neutrons. It's less abundant naturally but can be produced in a fusion reactor (we'll get to that later).\n",
       "\n",
       "2. **Overcoming Repulsion:** Atomic nuclei are positively charged (due to protons).  This means they naturally repel each other, like trying to push two magnets together with their north poles facing each other.  To overcome this electrical repulsion and force the nuclei close enough to fuse, you need **extremely high temperatures**.\n",
       "\n",
       "3. **Extreme Heat and Plasma:**  We're talking temperatures millions of degrees Celsius, many times hotter than the Sun's core! At these temperatures, atoms lose their electrons, forming a **plasma**, which is often called the fourth state of matter (after solid, liquid, and gas).  Plasma is a superheated, ionized gas where electrons and nuclei move independently.\n",
       "\n",
       "4. **Fusion Occurs:**  In this superheated plasma, nuclei are moving incredibly fast.  When they collide with enough force (due to the heat), they can overcome the electrical repulsion and get close enough for the **strong nuclear force** to take over.  The strong nuclear force is a short-range, very powerful force that binds protons and neutrons together within the nucleus.\n",
       "\n",
       "5. **Nuclei Combine and Energy Release:** When the strong nuclear force takes hold, the deuterium and tritium nuclei fuse together to form a **helium-4 nucleus (⁴He)**.  This new helium nucleus is slightly *less* massive than the combined mass of the original deuterium and tritium nuclei. This \"missing\" mass is converted into a tremendous amount of energy according to Einstein's famous equation, **E=mc²** (Energy = mass x the speed of light squared).  The \"c²\" term is huge, meaning a tiny bit of mass loss results in a massive energy release.\n",
       "\n",
       "6. **Neutron Production:**  In addition to the helium nucleus and energy, a **neutron** is also released during the deuterium-tritium fusion reaction.  This neutron carries away some of the energy produced.\n",
       "\n",
       "**Simplified Reaction Equation (Deuterium-Tritium):**\n",
       "\n",
       "²H (Deuterium) + ³H (Tritium)  →  ⁴He (Helium) + Neutron + Energy\n",
       "\n",
       "**Why is Nuclear Fusion Important for Clean Energy?**\n",
       "\n",
       "Nuclear fusion holds immense promise as a clean and sustainable energy source for several key reasons:\n",
       "\n",
       "* **Abundant Fuel:**\n",
       "    * **Deuterium:**  Is readily extracted from seawater.  There's enough deuterium in the oceans to potentially power the world for billions of years.\n",
       "    * **Tritium:** While less abundant naturally, tritium can be produced within the fusion reactor itself through a process called \"neutron breeding.\"  The neutrons released from the fusion reaction can be used to bombard lithium, which transmutes into tritium and helium. Lithium is also relatively abundant in the Earth's crust and seawater. This creates a closed fuel cycle, making fusion very sustainable.\n",
       "\n",
       "* **No Greenhouse Gas Emissions:** Fusion reactions themselves do not produce carbon dioxide (CO₂), methane (CH₄), or other greenhouse gases that contribute to climate change.  The primary product is helium, an inert and harmless gas.  This is a huge advantage over fossil fuels.\n",
       "\n",
       "* **Reduced Radioactive Waste:** While fusion reactors will produce some radioactive waste, it is significantly less and less long-lived compared to nuclear fission.\n",
       "    * **No Long-Lived Radioactive Waste Products:** Fusion doesn't produce the highly radioactive and long-lasting fission products (like strontium-90 or cesium-137) that are a major concern with current nuclear power plants.\n",
       "    * **Radioactive Components from Reactor Structure:**  The reactor components themselves will become radioactive due to neutron bombardment during operation. However, these materials are generally designed to have shorter half-lives, meaning the radioactivity decays relatively quickly (on the order of decades to centuries, rather than millennia for some fission waste).\n",
       "\n",
       "* **Inherent Safety:** Fusion reactors are considered inherently safer than fission reactors:\n",
       "    * **No Runaway Chain Reaction:** Fusion is not a chain reaction in the same way fission is. It requires precise conditions of temperature and pressure to be maintained. If those conditions are disrupted, the fusion reaction simply stops. There's no risk of a runaway reaction or a meltdown like in a fission reactor.\n",
       "    * **Small Fuel Quantity:**  A fusion reactor would only contain a very small amount of fuel at any given time (grams). If there were a malfunction, the reaction would quickly stop, and there's no large inventory of radioactive material to release.\n",
       "\n",
       "* **High Energy Output:** Fusion reactions release a tremendous amount of energy per unit of fuel, even greater than fission. This means a relatively small amount of fuel can generate a significant amount of power.\n",
       "\n",
       "**Challenges and Current Status:**\n",
       "\n",
       "Despite its immense potential, nuclear fusion is not yet a commercially viable energy source.  There are significant technical challenges:\n",
       "\n",
       "* **Achieving and Sustaining Fusion Conditions:** Creating and maintaining the extremely high temperatures and pressures needed for sustained fusion is incredibly difficult.\n",
       "* **Confinement:**  Keeping the superheated plasma stable and confined long enough for efficient fusion reactions to occur is a major engineering challenge.  Two main approaches are being explored:\n",
       "    * **Magnetic Confinement:** Using powerful magnetic fields to confine the plasma (e.g., tokamaks and stellarators).\n",
       "    * **Inertial Confinement:**  Using lasers or particle beams to compress and heat fuel pellets very rapidly (e.g., laser fusion).\n",
       "* **Net Energy Gain:**  While fusion has been achieved in experiments, achieving \"net energy gain\" (producing more energy from fusion than is required to heat and confine the plasma) is the crucial next step.  Significant progress is being made, and recent experiments have shown encouraging results, but consistent net energy gain is still a goal.\n",
       "* **Cost and Technology Development:** Developing the advanced technologies needed for fusion reactors is very expensive and requires ongoing research and development.\n",
       "\n",
       "**In Conclusion:**\n",
       "\n",
       "Nuclear fusion is a powerful and potentially transformative energy source that mimics the processes that power the Sun.  Its promise of abundant, clean, and safe energy is a major driver for ongoing research and development efforts worldwide. While significant challenges remain, progress is being made, and if successful, fusion could revolutionize our energy future and play a critical role in addressing climate change and securing a sustainable energy supply for generations to come."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.display import Markdown\n",
    "\n",
    "Markdown(result[\"messages\"][-1].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the tool that the judge can use to indicate the response is acceptable\n",
    "class Finish(TypedDict):\n",
    "    \"\"\"Tool for the judge to indicate the response is acceptable.\"\"\"\n",
    "\n",
    "    finish: bool\n",
    "\n",
    "\n",
    "# Define a more detailed critique prompt with specific evaluation criteria\n",
    "critique_prompt = \"\"\"You are an expert judge evaluating AI responses. Your task is to critique the AI assistant's latest response in the conversation below.\n",
    "\n",
    "Evaluate the response based on these criteria:\n",
    "1. Accuracy - Is the information correct and factual?\n",
    "2. Completeness - Does it fully address the user's query?\n",
    "3. Clarity - Is the explanation clear and well-structured?\n",
    "4. Helpfulness - Does it provide actionable and useful information?\n",
    "5. Safety - Does it avoid harmful or inappropriate content?\n",
    "\n",
    "If the response meets ALL criteria satisfactorily, set pass to True.\n",
    "\n",
    "If you find ANY issues with the response, do NOT set pass to True. Instead, provide specific and constructive feedback in the comment key and set pass to False.\n",
    "\n",
    "Be detailed in your critique so the assistant can understand exactly how to improve.\n",
    "\n",
    "<response>\n",
    "{outputs}\n",
    "</response>\"\"\"\n",
    "\n",
    "\n",
    "# Define the judge function with a more robust evaluation approach\n",
    "def judge_response(state, config):\n",
    "    \"\"\"Evaluate the assistant's response using a separate judge model.\"\"\"\n",
    "    evaluator = create_llm_as_judge(\n",
    "        prompt=critique_prompt,\n",
    "        model=\"openai:claude-3.7-sonnet\",\n",
    "        feedback_key=\"pass\",\n",
    "    )\n",
    "    eval_result = evaluator(outputs=state[\"messages\"][-1].content, inputs=None)\n",
    "    print(\"Eval result:\", eval_result)\n",
    "\n",
    "    if eval_result[\"score\"]:\n",
    "        print(\"✅ Response approved by judge\")\n",
    "        return\n",
    "    else:\n",
    "        # Otherwise, return the judge's critique as a new user message\n",
    "        print(\"⚠️ Judge requested improvements\")\n",
    "        return {\"messages\": [{\"role\": \"user\", \"content\": eval_result[\"comment\"]}]}\n",
    "\n",
    "\n",
    "# Define the judge graph\n",
    "judge_graph = (\n",
    "    StateGraph(MessagesState)\n",
    "    .add_node(judge_response)\n",
    "    .add_edge(START, \"judge_response\")\n",
    "    .add_edge(\"judge_response\", END)\n",
    "    .compile()\n",
    ")\n",
    "\n",
    "\n",
    "# Create the complete reflection graph\n",
    "reflection_app = create_reflection_graph(assistant_graph, judge_graph)\n",
    "reflection_app = reflection_app.compile()\n",
    "\n",
    "# Notice: Drawing Mermaid flowchart for the whole reflection graph is NOT supported yet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running example with reflection...\n",
      "Eval result: {'key': 'pass', 'score': True, 'comment': \"The AI response provides a comprehensive explanation of nuclear fusion as a clean energy source. Let me evaluate it based on the given criteria:\\n\\n1. Accuracy: The response provides scientifically accurate information about nuclear fusion, including the physics behind it (combining light nuclei to form heavier ones), the conditions required (extreme heat and pressure), confinement methods (magnetic and inertial), the D-T fusion reaction, and Einstein's mass-energy equivalence. The explanation of the challenges and current state of fusion technology is also accurate.\\n\\n2. Completeness: The response thoroughly addresses what nuclear fusion is, how it works, why it's important for clean energy, and the challenges it faces. It covers the scientific principles, practical applications, advantages, and limitations of fusion energy. The explanation is comprehensive and leaves no major aspects of the topic unexplored.\\n\\n3. Clarity: The explanation is exceptionally clear and well-structured. It uses a logical progression from basic concepts to more complex ones, with clear headings and bullet points. Technical concepts are explained in accessible language with helpful analogies (like comparing nuclear repulsion to pushing magnets together). The organization makes it easy to follow even for someone unfamiliar with the topic.\\n\\n4. Helpfulness: The response provides detailed, actionable information about fusion energy that would be valuable to someone wanting to understand this technology. It balances technical details with practical implications and gives a realistic assessment of both the potential and challenges of fusion energy.\\n\\n5. Safety: The content is entirely appropriate and contains no harmful information. It accurately discusses the safety advantages of fusion over fission (no chain reaction, less radioactive waste) without making misleading claims.\\n\\nThe response excels in all five criteria. It is scientifically accurate, comprehensive in its coverage, clearly structured and explained, highly informative and helpful, and completely safe in its content. Thus, the score should be: true.\"}\n",
      "✅ Response approved by judge\n"
     ]
    }
   ],
   "source": [
    "# Process the query through the reflection system\n",
    "print(\"Running example with reflection...\")\n",
    "result = reflection_app.invoke({\"messages\": example_query})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "👆 LangSmith Trace: https://smith.langchain.com/public/5350a152-8f4b-4024-8e3b-8ae63aaec9b5/r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Code Validation\n",
    "\n",
    "This example demonstrates how to use the reflection agent to validate and improve Python code. It uses Pyright for static type checking and error detection. The system:\n",
    "\n",
    "1. Takes a coding task as input\n",
    "2. Generates Python code using the main agent\n",
    "3. Validates the code using Pyright\n",
    "4. If errors are found, sends them back to the main agent for correction\n",
    "5. Repeats until the code passes validation\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"Example of a LangGraph application with code reflection capabilities using Pyright.\n",
    "\n",
    "Should install:\n",
    "\n",
    "```\n",
    "pip install langgraph-reflection langchain pyright\n",
    "```\n",
    "\"\"\"\n",
    "\n",
    "import json\n",
    "import os\n",
    "import subprocess\n",
    "import tempfile\n",
    "from typing import TypedDict, Annotated, Literal\n",
    "\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langgraph.graph import StateGraph, MessagesState, START, END\n",
    "from langgraph_reflection import create_reflection_graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "def analyze_with_pyright(code_string: str) -> dict:\n",
    "    \"\"\"Analyze Python code using Pyright for static type checking and errors.\n",
    "\n",
    "    Args:\n",
    "        code_string: The Python code to analyze as a string\n",
    "\n",
    "    Returns:\n",
    "        dict: The Pyright analysis results\n",
    "    \"\"\"\n",
    "    with tempfile.NamedTemporaryFile(suffix=\".py\", mode=\"w\", delete=False) as temp:\n",
    "        temp.write(code_string)\n",
    "        temp_path = temp.name\n",
    "\n",
    "    try:\n",
    "        result = subprocess.run(\n",
    "            [\n",
    "                \"pyright\",\n",
    "                \"--outputjson\",\n",
    "                \"--level\",\n",
    "                \"error\",  # Only report errors, not warnings\n",
    "                temp_path,\n",
    "            ],\n",
    "            capture_output=True,\n",
    "            text=True,\n",
    "        )\n",
    "\n",
    "        try:\n",
    "            return json.loads(result.stdout)\n",
    "        except json.JSONDecodeError:\n",
    "            return {\n",
    "                \"error\": \"Failed to parse Pyright output\",\n",
    "                \"raw_output\": result.stdout,\n",
    "            }\n",
    "    finally:\n",
    "        os.unlink(temp_path)\n",
    "\n",
    "\n",
    "def call_model(state: dict) -> dict:\n",
    "    \"\"\"Process the user query with a Claude 3 Sonnet model.\n",
    "\n",
    "    Args:\n",
    "        state: The current conversation state\n",
    "\n",
    "    Returns:\n",
    "        dict: Updated state with model response\n",
    "    \"\"\"\n",
    "    model = init_chat_model(model=\"openai:bedrock-c3.5-sonnet\")\n",
    "    return {\"messages\": model.invoke(state[\"messages\"])}\n",
    "\n",
    "\n",
    "# Define type classes for code extraction\n",
    "class ExtractPythonCode(TypedDict):\n",
    "    \"\"\"Type class for extracting Python code. The python_code field is the code to be extracted.\"\"\"\n",
    "\n",
    "    python_code: str\n",
    "\n",
    "\n",
    "class NoCode(TypedDict):\n",
    "    \"\"\"Type class for indicating no code was found.\"\"\"\n",
    "\n",
    "    no_code: bool\n",
    "\n",
    "\n",
    "# System prompt for the model\n",
    "SYSTEM_PROMPT = \"\"\"The below conversation is you conversing with a user to write some python code. Your final response is the last message in the list.\n",
    "\n",
    "Sometimes you will respond with code, othertimes with a question.\n",
    "\n",
    "If there is code - extract it into a single python script using ExtractPythonCode.\n",
    "\n",
    "If there is no code to extract - call NoCode.\"\"\"\n",
    "\n",
    "\n",
    "def try_running(state: dict) -> dict | None:\n",
    "    \"\"\"Attempt to run and analyze the extracted Python code.\n",
    "\n",
    "    Args:\n",
    "        state: The current conversation state\n",
    "\n",
    "    Returns:\n",
    "        dict | None: Updated state with analysis results if code was found\n",
    "    \"\"\"\n",
    "    model = init_chat_model(model=\"openai:claude-3.7-sonnet\")\n",
    "    extraction = model.bind_tools([ExtractPythonCode, NoCode])\n",
    "    er = extraction.invoke(\n",
    "        [{\"role\": \"system\", \"content\": SYSTEM_PROMPT}] + state[\"messages\"]\n",
    "    )\n",
    "    if len(er.tool_calls) == 0:\n",
    "        return None\n",
    "    tc = er.tool_calls[0]\n",
    "    if tc[\"name\"] != \"ExtractPythonCode\":\n",
    "        return None\n",
    "\n",
    "    result = analyze_with_pyright(tc[\"args\"][\"python_code\"])\n",
    "    print(result)\n",
    "    explanation = result[\"generalDiagnostics\"]\n",
    "\n",
    "    if result[\"summary\"][\"errorCount\"]:\n",
    "        return {\n",
    "            \"messages\": [\n",
    "                {\n",
    "                    \"role\": \"user\",\n",
    "                    \"content\": f\"I ran pyright and found this: {explanation}\\n\\n\"\n",
    "                    \"Try to fix it. Make sure to regenerate the entire code snippet. \"\n",
    "                    \"If you are not sure what is wrong, or think there is a mistake, \"\n",
    "                    \"you can ask me a question rather than generating code\",\n",
    "                }\n",
    "            ]\n",
    "        }\n",
    "\n",
    "\n",
    "def create_graphs():\n",
    "    \"\"\"Create and configure the assistant and judge graphs.\"\"\"\n",
    "    # Define the main assistant graph\n",
    "    assistant_graph = (\n",
    "        StateGraph(MessagesState)\n",
    "        .add_node(call_model)\n",
    "        .add_edge(START, \"call_model\")\n",
    "        .add_edge(\"call_model\", END)\n",
    "        .compile()\n",
    "    )\n",
    "\n",
    "    # Define the judge graph for code analysis\n",
    "    judge_graph = (\n",
    "        StateGraph(MessagesState)\n",
    "        .add_node(try_running)\n",
    "        .add_edge(START, \"try_running\")\n",
    "        .add_edge(\"try_running\", END)\n",
    "        .compile()\n",
    "    )\n",
    "\n",
    "    # Create the complete reflection graph\n",
    "    return create_reflection_graph(assistant_graph, judge_graph).compile()\n",
    "\n",
    "\n",
    "reflection_app = create_graphs()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running example with reflection...\n",
      "{'version': '1.1.396', 'time': '1741763116976', 'generalDiagnostics': [{'file': '/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/tmp3gg9af_f.py', 'severity': 'error', 'message': 'Import \"langchain_community.vectorstores\" could not be resolved', 'range': {'start': {'line': 5, 'character': 5}, 'end': {'line': 5, 'character': 37}}, 'rule': 'reportMissingImports'}, {'file': '/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/tmp3gg9af_f.py', 'severity': 'error', 'message': 'Expression of type \"None\" cannot be assigned to parameter of type \"List[Any]\"\\n\\xa0\\xa0\"None\" is not assignable to \"List[Any]\"', 'range': {'start': {'line': 85, 'character': 53}, 'end': {'line': 85, 'character': 57}}, 'rule': 'reportArgumentType'}], 'summary': {'filesAnalyzed': 1, 'errorCount': 2, 'warningCount': 0, 'informationCount': 0, 'timeInSec': 0.573}}\n",
      "Result: {'messages': [HumanMessage(content='Write a LangGraph RAG app', additional_kwargs={}, response_metadata={}, id='ad7efb3d-db68-4939-ae7e-dfc3b6e47e77'), AIMessage(content='Here\\'s an example of a LangGraph RAG (Retrieval Augmented Generation) app that implements a simple question-answering system with memory:\\n\\n```python\\nfrom typing import Dict, List, Tuple, Any\\nfrom langgraph.graph import Graph, StateGraph\\nfrom langchain_core.messages import HumanMessage, AIMessage\\nfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\\nfrom langchain_openai import ChatOpenAI\\nfrom langchain_community.vectorstores import FAISS\\nfrom langchain_openai import OpenAIEmbeddings\\nfrom langchain_text_splitters import RecursiveCharacterTextSplitter\\nimport os\\n\\n# Set your OpenAI API key\\nos.environ[\"OPENAI_API_KEY\"] = \"your-api-key-here\"\\n\\n# Initialize components\\nembeddings = OpenAIEmbeddings()\\nllm = ChatOpenAI(temperature=0)\\n\\n# Sample knowledge base\\nknowledge_base = \"\"\"\\nPython is a high-level, interpreted programming language.\\nIt was created by Guido van Rossum and released in 1991.\\nPython emphasizes code readability with its notable use of significant whitespace.\\nPython features a dynamic type system and automatic memory management.\\n\"\"\"\\n\\n# Create vector store\\ndef create_vector_store(text: str) -> FAISS:\\n    text_splitter = RecursiveCharacterTextSplitter(\\n        chunk_size=500,\\n        chunk_overlap=50\\n    )\\n    chunks = text_splitter.split_text(text)\\n    vector_store = FAISS.from_texts(chunks, embeddings)\\n    return vector_store\\n\\nvector_store = create_vector_store(knowledge_base)\\n\\n# Define state type\\nclass State(Dict):\\n    \"\"\"The state of the RAG system.\"\"\"\\n    messages: List[Any]\\n    context: str\\n    \\n# Define RAG functions\\ndef retrieve(state: State) -> State:\\n    \"\"\"Retrieve relevant documents based on the latest message.\"\"\"\\n    latest_message = state[\"messages\"][-1].content\\n    docs = vector_store.similarity_search(latest_message, k=2)\\n    context = \"\\\\n\".join(doc.page_content for doc in docs)\\n    state[\"context\"] = context\\n    return state\\n\\ndef generate(state: State) -> State:\\n    \"\"\"Generate a response using the context and conversation history.\"\"\"\\n    prompt = ChatPromptTemplate.from_messages([\\n        (\"system\", \"You are a helpful AI assistant. Use the following context to answer the user\\'s question:\\\\n\\\\n{context}\"),\\n        MessagesPlaceholder(variable_name=\"messages\"),\\n    ])\\n    \\n    response = llm.invoke(\\n        prompt.format_messages(\\n            context=state[\"context\"],\\n            messages=state[\"messages\"]\\n        )\\n    )\\n    \\n    state[\"messages\"].append(AIMessage(content=response.content))\\n    return state\\n\\n# Create the graph\\nworkflow = StateGraph(State)\\n\\n# Add nodes\\nworkflow.add_node(\"retrieve\", retrieve)\\nworkflow.add_node(\"generate\", generate)\\n\\n# Add edges\\nworkflow.set_entry_point(\"retrieve\")\\nworkflow.add_edge(\"retrieve\", \"generate\")\\nworkflow.set_finish_point(\"generate\")\\n\\n# Compile the graph\\nchain = workflow.compile()\\n\\n# Function to run the RAG system\\ndef run_rag(question: str, chat_history: List[Any] = None) -> Tuple[str, List[Any]]:\\n    if chat_history is None:\\n        chat_history = []\\n    \\n    # Initialize state\\n    state = {\\n        \"messages\": chat_history + [HumanMessage(content=question)],\\n        \"context\": \"\"\\n    }\\n    \\n    # Run the chain\\n    result = chain.invoke(state)\\n    return result[\"messages\"][-1].content, result[\"messages\"]\\n\\n# Example usage\\nif __name__ == \"__main__\":\\n    # First question\\n    response, history = run_rag(\"What is Python?\")\\n    print(\"Q1: What is Python?\")\\n    print(\"A1:\", response)\\n    print()\\n    \\n    # Follow-up question\\n    response, history = run_rag(\"Who created it?\", history)\\n    print(\"Q2: Who created it?\")\\n    print(\"A2:\", response)\\n```\\n\\nThis LangGraph RAG app includes the following components:\\n\\n1. **Vector Store Setup**: Creates a FAISS vector store from the knowledge base using OpenAI embeddings.\\n\\n2. **State Management**: Defines a State class to maintain the conversation history and retrieved context.\\n\\n3. **RAG Functions**:\\n   - `retrieve`: Fetches relevant documents based on the user\\'s question\\n   - `generate`: Generates a response using the retrieved context and conversation history\\n\\n4. **Graph Structure**: Creates a simple sequential graph with retrieve → generate flow.\\n\\nTo use this app:\\n\\n1. Install required packages:\\n```bash\\npip install langgraph langchain-core langchain-openai langchain-community faiss-cpu\\n```\\n\\n2. Replace `\"your-api-key-here\"` with your actual OpenAI API key.\\n\\n3. Run the script to see example questions and answers.\\n\\nYou can extend this basic implementation by:\\n\\n1. Adding more sophisticated retrieval methods\\n2. Implementing error handling\\n3. Adding document preprocessing\\n4. Including memory management\\n5. Adding branching logic based on question type\\n6. Implementing feedback loops\\n7. Adding document sources tracking\\n\\nExample output might look like:\\n```\\nQ1: What is Python?\\nA1: Python is a high-level, interpreted programming language that emphasizes code readability through its use of significant whitespace. It features a dynamic type system and automatic memory management.\\n\\nQ2: Who created it?\\nA2: Python was created by Guido van Rossum and was released in 1991.\\n```\\n\\nThis is a basic implementation that you can build upon based on your specific needs. You might want to add error handling, more sophisticated retrieval methods, or additional processing steps depending on your use case.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 1412, 'prompt_tokens': 15, 'total_tokens': 1427, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'anthropic.claude-3-5-sonnet-20241022-v2:0', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-d457bef5-d8be-49a9-a169-426fbad90baf-0', usage_metadata={'input_tokens': 15, 'output_tokens': 1412, 'total_tokens': 1427, 'input_token_details': {}, 'output_token_details': {}}), HumanMessage(content='I ran pyright and found this: [{\\'file\\': \\'/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/tmp3gg9af_f.py\\', \\'severity\\': \\'error\\', \\'message\\': \\'Import \"langchain_community.vectorstores\" could not be resolved\\', \\'range\\': {\\'start\\': {\\'line\\': 5, \\'character\\': 5}, \\'end\\': {\\'line\\': 5, \\'character\\': 37}}, \\'rule\\': \\'reportMissingImports\\'}, {\\'file\\': \\'/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/tmp3gg9af_f.py\\', \\'severity\\': \\'error\\', \\'message\\': \\'Expression of type \"None\" cannot be assigned to parameter of type \"List[Any]\"\\\\n\\\\xa0\\\\xa0\"None\" is not assignable to \"List[Any]\"\\', \\'range\\': {\\'start\\': {\\'line\\': 85, \\'character\\': 53}, \\'end\\': {\\'line\\': 85, \\'character\\': 57}}, \\'rule\\': \\'reportArgumentType\\'}]\\n\\nTry to fix it. Make sure to regenerate the entire code snippet. If you are not sure what is wrong, or think there is a mistake, you can ask me a question rather than generating code', additional_kwargs={}, response_metadata={}, id='437eae6d-72b2-437c-aeae-516d56feee43'), AIMessage(content=\"I notice two issues from the Pyright output:\\n\\n1. The import for vectorstores seems incorrect - I should verify the correct import path\\n2. There's a type error with the chat_history parameter defaulting to None\\n\\nBefore I generate a fix, could you confirm if you have the following packages installed?\\n- langchain-community\\n- langgraph\\n- langchain-openai\\n- faiss-cpu\\n\\nAlso, I want to verify the correct import path for FAISS from langchain-community. Would you be able to check the current version of langchain-community you're using?\\n\\nThis will help me provide a correct solution that works with the latest package versions.\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 149, 'prompt_tokens': 1765, 'total_tokens': 1914, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'anthropic.claude-3-5-sonnet-20241022-v2:0', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-e7b7e34a-99cc-40c5-8b55-9a0e43bfc789-0', usage_metadata={'input_tokens': 1765, 'output_tokens': 149, 'total_tokens': 1914, 'input_token_details': {}, 'output_token_details': {}})]}\n"
     ]
    }
   ],
   "source": [
    "\"\"\"Run an example query through the reflection system.\"\"\"\n",
    "example_query = [\n",
    "    {\n",
    "        \"role\": \"user\",\n",
    "        \"content\": \"Write a LangGraph RAG app\",\n",
    "    }\n",
    "]\n",
    "\n",
    "print(\"Running example with reflection...\")\n",
    "result = reflection_app.invoke({\"messages\": example_query})\n",
    "print(\"Result:\", result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "👆 LangSmith Trace: https://smith.langchain.com/public/92789bf1-585d-4249-9dc8-92668c6cf4fc/r"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Write a LangGraph RAG app\n",
      "----------------------------------------------------------------------------------------------------\n",
      "Here's an example of a LangGraph RAG (Retrieval Augmented Generation) app that implements a simple question-answering system with memory:\n",
      "\n",
      "```python\n",
      "from typing import Dict, List, Tuple, Any\n",
      "from langgraph.graph import Graph, StateGraph\n",
      "from langchain_core.messages import HumanMessage, AIMessage\n",
      "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
      "from langchain_openai import ChatOpenAI\n",
      "from langchain_community.vectorstores import FAISS\n",
      "from langchain_openai import OpenAIEmbeddings\n",
      "from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
      "import os\n",
      "\n",
      "# Set your OpenAI API key\n",
      "os.environ[\"OPENAI_API_KEY\"] = \"your-api-key-here\"\n",
      "\n",
      "# Initialize components\n",
      "embeddings = OpenAIEmbeddings()\n",
      "llm = ChatOpenAI(temperature=0)\n",
      "\n",
      "# Sample knowledge base\n",
      "knowledge_base = \"\"\"\n",
      "Python is a high-level, interpreted programming language.\n",
      "It was created by Guido van Rossum and released in 1991.\n",
      "Python emphasizes code readability with its notable use of significant whitespace.\n",
      "Python features a dynamic type system and automatic memory management.\n",
      "\"\"\"\n",
      "\n",
      "# Create vector store\n",
      "def create_vector_store(text: str) -> FAISS:\n",
      "    text_splitter = RecursiveCharacterTextSplitter(\n",
      "        chunk_size=500,\n",
      "        chunk_overlap=50\n",
      "    )\n",
      "    chunks = text_splitter.split_text(text)\n",
      "    vector_store = FAISS.from_texts(chunks, embeddings)\n",
      "    return vector_store\n",
      "\n",
      "vector_store = create_vector_store(knowledge_base)\n",
      "\n",
      "# Define state type\n",
      "class State(Dict):\n",
      "    \"\"\"The state of the RAG system.\"\"\"\n",
      "    messages: List[Any]\n",
      "    context: str\n",
      "    \n",
      "# Define RAG functions\n",
      "def retrieve(state: State) -> State:\n",
      "    \"\"\"Retrieve relevant documents based on the latest message.\"\"\"\n",
      "    latest_message = state[\"messages\"][-1].content\n",
      "    docs = vector_store.similarity_search(latest_message, k=2)\n",
      "    context = \"\\n\".join(doc.page_content for doc in docs)\n",
      "    state[\"context\"] = context\n",
      "    return state\n",
      "\n",
      "def generate(state: State) -> State:\n",
      "    \"\"\"Generate a response using the context and conversation history.\"\"\"\n",
      "    prompt = ChatPromptTemplate.from_messages([\n",
      "        (\"system\", \"You are a helpful AI assistant. Use the following context to answer the user's question:\\n\\n{context}\"),\n",
      "        MessagesPlaceholder(variable_name=\"messages\"),\n",
      "    ])\n",
      "    \n",
      "    response = llm.invoke(\n",
      "        prompt.format_messages(\n",
      "            context=state[\"context\"],\n",
      "            messages=state[\"messages\"]\n",
      "        )\n",
      "    )\n",
      "    \n",
      "    state[\"messages\"].append(AIMessage(content=response.content))\n",
      "    return state\n",
      "\n",
      "# Create the graph\n",
      "workflow = StateGraph(State)\n",
      "\n",
      "# Add nodes\n",
      "workflow.add_node(\"retrieve\", retrieve)\n",
      "workflow.add_node(\"generate\", generate)\n",
      "\n",
      "# Add edges\n",
      "workflow.set_entry_point(\"retrieve\")\n",
      "workflow.add_edge(\"retrieve\", \"generate\")\n",
      "workflow.set_finish_point(\"generate\")\n",
      "\n",
      "# Compile the graph\n",
      "chain = workflow.compile()\n",
      "\n",
      "# Function to run the RAG system\n",
      "def run_rag(question: str, chat_history: List[Any] = None) -> Tuple[str, List[Any]]:\n",
      "    if chat_history is None:\n",
      "        chat_history = []\n",
      "    \n",
      "    # Initialize state\n",
      "    state = {\n",
      "        \"messages\": chat_history + [HumanMessage(content=question)],\n",
      "        \"context\": \"\"\n",
      "    }\n",
      "    \n",
      "    # Run the chain\n",
      "    result = chain.invoke(state)\n",
      "    return result[\"messages\"][-1].content, result[\"messages\"]\n",
      "\n",
      "# Example usage\n",
      "if __name__ == \"__main__\":\n",
      "    # First question\n",
      "    response, history = run_rag(\"What is Python?\")\n",
      "    print(\"Q1: What is Python?\")\n",
      "    print(\"A1:\", response)\n",
      "    print()\n",
      "    \n",
      "    # Follow-up question\n",
      "    response, history = run_rag(\"Who created it?\", history)\n",
      "    print(\"Q2: Who created it?\")\n",
      "    print(\"A2:\", response)\n",
      "```\n",
      "\n",
      "This LangGraph RAG app includes the following components:\n",
      "\n",
      "1. **Vector Store Setup**: Creates a FAISS vector store from the knowledge base using OpenAI embeddings.\n",
      "\n",
      "2. **State Management**: Defines a State class to maintain the conversation history and retrieved context.\n",
      "\n",
      "3. **RAG Functions**:\n",
      "   - `retrieve`: Fetches relevant documents based on the user's question\n",
      "   - `generate`: Generates a response using the retrieved context and conversation history\n",
      "\n",
      "4. **Graph Structure**: Creates a simple sequential graph with retrieve → generate flow.\n",
      "\n",
      "To use this app:\n",
      "\n",
      "1. Install required packages:\n",
      "```bash\n",
      "pip install langgraph langchain-core langchain-openai langchain-community faiss-cpu\n",
      "```\n",
      "\n",
      "2. Replace `\"your-api-key-here\"` with your actual OpenAI API key.\n",
      "\n",
      "3. Run the script to see example questions and answers.\n",
      "\n",
      "You can extend this basic implementation by:\n",
      "\n",
      "1. Adding more sophisticated retrieval methods\n",
      "2. Implementing error handling\n",
      "3. Adding document preprocessing\n",
      "4. Including memory management\n",
      "5. Adding branching logic based on question type\n",
      "6. Implementing feedback loops\n",
      "7. Adding document sources tracking\n",
      "\n",
      "Example output might look like:\n",
      "```\n",
      "Q1: What is Python?\n",
      "A1: Python is a high-level, interpreted programming language that emphasizes code readability through its use of significant whitespace. It features a dynamic type system and automatic memory management.\n",
      "\n",
      "Q2: Who created it?\n",
      "A2: Python was created by Guido van Rossum and was released in 1991.\n",
      "```\n",
      "\n",
      "This is a basic implementation that you can build upon based on your specific needs. You might want to add error handling, more sophisticated retrieval methods, or additional processing steps depending on your use case.\n",
      "----------------------------------------------------------------------------------------------------\n",
      "I ran pyright and found this: [{'file': '/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/tmp3gg9af_f.py', 'severity': 'error', 'message': 'Import \"langchain_community.vectorstores\" could not be resolved', 'range': {'start': {'line': 5, 'character': 5}, 'end': {'line': 5, 'character': 37}}, 'rule': 'reportMissingImports'}, {'file': '/var/folders/f3/zw7t8v4s6j3gd6hyktrdg85r0000gn/T/tmp3gg9af_f.py', 'severity': 'error', 'message': 'Expression of type \"None\" cannot be assigned to parameter of type \"List[Any]\"\\n\\xa0\\xa0\"None\" is not assignable to \"List[Any]\"', 'range': {'start': {'line': 85, 'character': 53}, 'end': {'line': 85, 'character': 57}}, 'rule': 'reportArgumentType'}]\n",
      "\n",
      "Try to fix it. Make sure to regenerate the entire code snippet. If you are not sure what is wrong, or think there is a mistake, you can ask me a question rather than generating code\n",
      "----------------------------------------------------------------------------------------------------\n",
      "I notice two issues from the Pyright output:\n",
      "\n",
      "1. The import for vectorstores seems incorrect - I should verify the correct import path\n",
      "2. There's a type error with the chat_history parameter defaulting to None\n",
      "\n",
      "Before I generate a fix, could you confirm if you have the following packages installed?\n",
      "- langchain-community\n",
      "- langgraph\n",
      "- langchain-openai\n",
      "- faiss-cpu\n",
      "\n",
      "Also, I want to verify the correct import path for FAISS from langchain-community. Would you be able to check the current version of langchain-community you're using?\n",
      "\n",
      "This will help me provide a correct solution that works with the latest package versions.\n",
      "----------------------------------------------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "for message in result[\"messages\"]:\n",
    "    print(message.content)\n",
    "    print(\"-\"*100)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
